/*
 * Copyright (c) 2024 Termony Co., Ltd. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "hdf_vuart.h"
#include "hdf_base.h"
#include "hdf_log.h"

#include "uart_core.h"
#include "osal_mem.h"
#include "uart_if.h"
#include "securec.h"

#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "VUART"

static struct VuartCb *HDF_GetVuartCb(struct UartHost *host, bool initCheck)
{
    if (host == NULL || host->priv == NULL) {
        HDF_LOGE("%s: host or host->priv or data is NULL.", __func__);
        return NULL;
    }

    struct VuartCb *vuartCb = (struct VuartCb *)host->priv;

    if (initCheck) {
        if (!vuartCb->initFlag) {
            HDF_LOGE("%s: The %s is not initialized.", __func__, vuartCb->cfg.name);
            return NULL;
        }
    }

    return vuartCb;
}

static int32_t HDF_VuartHostInit(struct UartHost *host)
{
    struct VuartCb *vuartCb = HDF_GetVuartCb(host, false);
    if (vuartCb == NULL) {
        HDF_LOGE("%s: HDF_GetVuartCb is failed.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    if (HDF_VuartInit(vuartCb) != HDF_SUCCESS) {
        HDF_LOGE("%s: [%s] HDF_VuartInit is failed.", __func__, vuartCb->cfg.name);
        return HDF_FAILURE;
    }

    vuartCb->initFlag = true;
    return HDF_SUCCESS;
}

static int32_t HDF_VuartHostDeinit(struct UartHost *host)
{
    struct VuartCb *vuartCb = HDF_GetVuartCb(host, true);
    if (vuartCb == NULL) {
        HDF_LOGE("%s: HDF_GetVuartCb is failed.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    if (HDF_VuartDeinit(vuartCb) != HDF_SUCCESS) {
        HDF_LOGE("%s: [%s] HDF_UartDeInit is failed.", __func__, vuartCb->cfg.name);
        return HDF_FAILURE;
    }

    vuartCb->initFlag = false;
    return HDF_SUCCESS;
}

static int32_t HDF_VuartHostRead(struct UartHost *host, uint8_t *data, uint32_t size)
{
    struct VuartCb *vuartCb = HDF_GetVuartCb(host, true);
    if (vuartCb == NULL || data == NULL || size == 0) {
        HDF_LOGE("%s: vuartCb or data or size is NULL.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    if (DListIsEmpty(&(vuartCb->recv))) {
        return 0;
    }

    struct VuartDataItem *dataItem = DLIST_FIRST_ENTRY(&(vuartCb->recv), struct VuartDataItem, list);
    uint8_t *buffer = ((uint8_t *)dataItem) + sizeof(struct VuartDataItem);
    uint32_t len = (dataItem->used <= size) ? dataItem->used : size;

    OsalMutexLock(&(vuartCb->rxMux));
    (void)memcpy_s(data, size, buffer, len);
    if (dataItem->used <= size) {
        DListRemove(&(dataItem->list));
        OsalMemFree(dataItem);
    } else {
        (void)memcpy_s(buffer, dataItem->size, buffer + len, dataItem->used - len);
        dataItem->used -= len;
    }
    OsalMutexUnlock(&(vuartCb->rxMux));

    return len;
}

static int32_t HDF_VuartHostWrite(struct UartHost *host, uint8_t *data, uint32_t size)
{
    int ret;
    struct VuartCb *vuartCb = HDF_GetVuartCb(host, true);
    if (vuartCb == NULL || data == NULL || size == 0) {
        HDF_LOGE("%s: vuartCb or data or size is NULL.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    OsalMutexLock(&(vuartCb->txMux));
    ret = openamp_vuart_send((const uint8_t *)data, (size_t)size);
    OsalMutexUnlock(&(vuartCb->txMux));
    return ret;
}

static struct UartHostMethod g_hdfVuartHostOps = {
    .Init = HDF_VuartHostInit,
    .Deinit = HDF_VuartHostDeinit,
    .Read = HDF_VuartHostRead,
    .Write = HDF_VuartHostWrite,
};

static int32_t HDF_VartDriverBind(struct HdfDeviceObject *device)
{
    return HDF_SUCCESS;
}

static int32_t HDF_VartDriverInit(struct HdfDeviceObject *device)
{
    if (device == NULL) {
        HDF_LOGE("%s: device is NULL.", __func__);
        return HDF_ERR_INVALID_PARAM;
    }

    struct UartHost *host = UartHostCreate(device);
    if (host == NULL) {
        HDF_LOGE("%s: UartHostCreate failed.", __func__);
        return HDF_FAILURE;
    }

    struct VuartCb *vuartCb = (struct VuartCb *)OsalMemCalloc(sizeof(struct VuartCb));
    if (vuartCb == NULL) {
        HDF_LOGE("%s: OsalMemCalloc(size = %d) is failed.", __func__, sizeof(struct VuartCb));
        UartHostDestroy(host);
        return HDF_FAILURE;
    }

    vuartCb->host = host;
    host->priv = vuartCb;
    host->method = &g_hdfVuartHostOps;
    if (HDF_VuartHostParser(host, device) != HDF_SUCCESS) {
        HDF_LOGE("%s: HDF_VuartHostParser is failed.", __func__);
        OsalMemFree(vuartCb);
        UartHostDestroy(host);
        return HDF_FAILURE;
    }

    return HDF_SUCCESS;
}

static void HDF_VartDriverRelease(struct HdfDeviceObject *device)
{
    struct UartHost *host = UartHostFromDevice(device);
    if (host == NULL) {
        HDF_LOGE("%s: host is NULL.", __func__);
        return;
    }

    UartHostDestroy(host);
}

static struct HdfDriverEntry g_hdfVuart = {
    .moduleVersion = 1,
    .moduleName = "HDF_PLATFORM_VUART",
    .Bind = HDF_VartDriverBind,
    .Init = HDF_VartDriverInit,
    .Release = HDF_VartDriverRelease,
};
HDF_INIT(g_hdfVuart);
